#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <functional>
#include <unordered_map>

#include "log.hpp"
#include "Comm.hpp"
#include "Socket.hpp"
#include "Epoller.hpp"

class Connection;
class TcpServer;

uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);

const int maxnum = 128;
const int defaultport = 8888;
const int g_buffer_size = 128;

using func_t = std::function<void(std::shared_ptr<Connection>)>;

class Connection
{
public:
    Connection(int sock)
        : _sock(sock)
    {
    }

    void SetCallback(func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

    void AppendInbuffer(const std::string &info)
    {
        _inbuffer += info;
    }

    void AppendOutbuffer(const std::string &info)
    {
        _outbuffer += info;
    }

    int Getsock()
    {
        return _sock;
    }

    void SetSharePtr(std::weak_ptr<TcpServer> tcp_server_ptr)
    {
        _tcp_server_str = tcp_server_ptr;
    }

    std::string &Inbuffer()
    {
        return _inbuffer;
    }

    std::string &Outbuffer()
    {
        return _outbuffer;
    }

    ~Connection()
    {
    }

private:
    int _sock;
    std::string _inbuffer;  // 读缓冲区
    std::string _outbuffer; // 写缓冲区

public:
    func_t _recv_cb;   // 读回调，当读事件就绪自动调用，下面也是一样的
    func_t _send_cb;   // 写回调
    func_t _except_cb; // 错误回调

    // 这是一个指向底层TcpServer对象的回指指针
    std::weak_ptr<TcpServer> _tcp_server_str;

    // 方便打印日志
    std::string _ip;
    uint16_t _port;
};

class TcpServer : public std::enable_shared_from_this<TcpServer>, public nocopy
{
public:
    TcpServer(const uint16_t port, func_t OnMessage)
        : _epoller_ptr(new Epoller()),
          _listensock_ptr(new Sock()),
          _port(port),
          _quit(true),
          _OnMessage(OnMessage)
    {
    }

    void Init()
    {
        // 监听套接字创建绑定监听，并设置成非阻塞
        _listensock_ptr->Socket();
        SetNonBlockOrDie(_listensock_ptr->Getsockfd());
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();

        // 将监听套接字进行AddConnection，设置回调之类的
        AddConnection(_listensock_ptr->Getsockfd(), EVENT_IN,
                      std::bind(&TcpServer::Accepter, this, std::placeholders::_1),
                      nullptr, nullptr, "0.0.0.0", _port);
        // 理解上述的bind
         // 创建一个函数对象，当调用这个函数对象时，\
        会调用当前对象（this 所指的对象）的 TcpServer::Accepter 成员函数，\
        并且调用时需要传入一个参数（由 placeholders::_1 占位）\
        \
        &TcpServer::Accepter：这是要绑定的成员函数指针，TcpServer 是一个类，Accepter 是该类中的一个成员函数\
        this：表示将成员函数 Accepter 绑定到当前对象（即调用 bind 时所在的对象）\
              因为成员函数需要通过对象来调用，所以这里传递 this 指针，使得绑定后的函数对象在调用时，\
              是针对当前对象的 Accepter 方法\
        placeholders::_1：这是 std::placeholders 中的占位符，用于表示绑定后的函数对象在被调用时，\
        需要接收一个参数，这个参数会传递给 Accepter 函数
    }

    // 7个参数...
    void AddConnection(int sock, uint32_t event,
                       func_t recv_rb, func_t send_cb, func_t except_cb,
                       const string &ip = "0.0.0.0", uint16_t port = 8080)
    {
        // 创建Connection智能指针对象
        // std::shared_ptr<Connection> new_connection = make_shared<Connection>(sock);
        std::shared_ptr<Connection> new_connection(new Connection(sock));

        new_connection->SetSharePtr(shared_from_this());

        // 设置回调
        new_connection->_recv_cb = recv_rb;
        new_connection->_send_cb = send_cb;
        new_connection->_except_cb = except_cb;
        // 设置IP与端口号
        new_connection->_ip = ip;
        new_connection->_port = port;
        // 添加到哈希表维护的数组中实现映射
        _connections.insert(std::make_pair(sock, new_connection));
        // 将关心的事件添加到内核中
        int n = _epoller_ptr->Epoll_Update(EPOLL_CTL_ADD, sock, event);
        // Debug日志，add a new connection success, sockfd is : %d"
        lg(DEBUG, "成功增加一个链接,sockfd is : %d", sock);
    }
    // listen套接字的Accept方法\
    连接管理器
    void Accepter(shared_ptr<Connection> connection)
    {
        while (true)
        {
            // accept 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = ::accept(connection->Getsock(), (struct sockaddr *)&peer, &len); // ？？？这不能写死为监听套接字的描述符吗
            if (sock > 0)
            {
                // 网络序列转主机序列，端口和IP地址都要转，用来AddConnection传参
                uint16_t peerport = ntohs(peer.sin_port);
                char ipbuf[128];
                inet_ntop(AF_INET, &peer.sin_addr.s_addr, ipbuf, sizeof(ipbuf));
                lg(DEBUG, "得到一个新的客户端连接,客户端IP:%s,客户端端口号:%d,客户端套接字:%d", ipbuf, peerport, sock);
                // 将得到的连接设置成非阻塞
                SetNonBlockOrDie(sock);
                // AddConnection进行回调，listensock只需要recv_rb，但是其他的sock，读，写，处理异常都要有
                AddConnection(sock, EVENT_IN,
                              std::bind(&TcpServer::Recver, this, std::placeholders::_1), 
                              std::bind(&TcpServer::Sender, this, std::placeholders::_1), 
                              std::bind(&TcpServer::Excepter, this, std::placeholders::_1), 
                              ipbuf, peerport);
            }
            else
            {
                // 根据errno进行判断
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                    break;
                // 获取连接失败，可能是信号中断(EINTR)，这样就进行continue，\
                如果是底层没有数据也就是读完了，把底层所有连接都拿上来了(EWOULDBLOCK)就break\
                也可能就是出错也break
            }
        }
    }

    void Recver(shared_ptr<Connection> connection)
    {
        // 得到当前文件描述符
        int fd = connection->Getsock();
        // ET模式，一直读读读，直到把数据读完
        while (true)
        {
            // 创建接收数组并且初始化,然后用recv将数据读到数组中
            char buffer[g_buffer_size];
            memset(buffer, 0, sizeof(buffer));
            ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0);
            // 读取成功，不断将数据append到接收缓冲区中
            if (n > 0)
            {
                connection->AppendInbuffer(buffer);
            }
            // == 0对方把链接关了,接着进入异常处理流程
            else if (n == 0)
            {
                lg(INFO, "sockfd: %d, client info %s : %d quit...", fd, connection->_ip.c_str(), connection->_port);
                connection->_except_cb(connection);
                return;
            }
            // < 0读取出错
            else
            {
                if (errno == EWOULDBLOCK)
                    break; // 底层没有数据也就是读完了，把底层所有连接都拿上来了
                else if (errno == EINTR)
                    continue; // 获取连接失败，可能是信号中断(EINTR)，这样就进行continue
                else          // 出错也break,然后进入异常处理流程
                {
                    lg(WARNING, "sockfd: %d, client info %s : %d recv error...", connection->_ip.c_str(), connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        // 到这里是读完了底层数据读完了，那么就交给上层进行处理
        _OnMessage(connection);
    }

    void Sender(shared_ptr<Connection> connection)
    {
        // 得到发送缓冲区
        std::string &outbuffer = connection->Outbuffer();
        int fd = connection->Getsock();
        // 一直发发发，直到发完为止
        while (true)
        {
            ssize_t n = send(fd, outbuffer.c_str(), outbuffer.size(), 0);
            if (n > 0) // 发送成功
            {
                outbuffer.erase(0, n);
                if (outbuffer.empty())
                    break;
            }
            else if (n == 0)
            {
                connection->_except_cb(connection);
                return;
            }
            else // n < 0
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    lg(WARNING, "sockfd: %d, client info %s : %d send error...", connection->_ip.c_str(), connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        // 走到这有两种情况，要么是成功发送完，要么是非阻塞模式下缓冲区已满，暂时无法发送更多数据
        // 简单来说就是要么数据全发完了，要么当前发不出去了，此时就需要关心写事件
        if (!outbuffer.empty())
        {
            // 此时就证明还有数据没有发完，但是底层的缓冲区已经没有数据了
            // 那么就需要关心写事件是否就绪
            EnableEvent(fd, true, true);
        }
        else
        {
            // 此时就证明数据写完了，没有数据了，然后关闭对写时间的关心
            EnableEvent(fd, true, false);
        }
    }

    void Excepter(shared_ptr<Connection> connection)
    {
        int sockfd = connection->Getsock();
        lg(DEBUG, "Excepter hander sockfd : %d, client info %s : %d Excepter hander...",
           sockfd, connection->_ip.c_str(), connection->_port);
        // 进行异常处理
        // 移除关心事件
        _epoller_ptr->Epoll_Update(EPOLL_CTL_DEL, sockfd, 0);
        // 关闭异常的文件描述符
        lg(DEBUG, "close %d done...\n", sockfd);
        close(sockfd);
        // 将fd->connection映射表中将对应的连接进行移除,也就是从unordered_map中移除
        lg(DEBUG, "remove %d from _connections...\n", sockfd);
        _connections.erase(sockfd);
    }

    void EnableEvent(int sock, bool readable, bool writeable)
    {
        uint32_t events = 0;
        events |= ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        _epoller_ptr->Epoll_Update(EPOLL_CTL_MOD, sock, events);
    }

    bool IsConnectionSafe(int fd)
    {
        auto iter = _connections.find(fd);
        if (iter == _connections.end())
            return false;
        else
            return true;
    }

    void Dispatcher()
    {
        // 通过epoll获取底层就绪的事件
        _epoller_ptr->Epoll_Wait(_revs, maxnum);
        // 对拿上来的连接进行处理 for
        for (int i = 0; i < maxnum; i++)
        {
            // 拿到当前连接所关心的事件和fd
            int fd = _revs[i].data.fd;
            uint32_t event = _revs[i].events;
            // 统一把事件异常转化为读写问题(EPOLLERR和EPOLLHUP)
            if (event & EPOLLERR)
                event |= (EPOLLIN | EPOLLOUT);
            if (event & EPOLLHUP)
                event |= (EPOLLIN | EPOLLOUT);
            // 接着只需要处理EPOLLIN和EPOLLOUT，如果是并且在哈希表中真的存在就检测当前连接的读回调是否被设置，\
                如果被设置了就将事件派发给读回调
            if ((event & EPOLLIN) && (IsConnectionSafe(fd)))
            {
                if (_connections[fd]->_recv_cb)
                    _connections[fd]->_recv_cb(_connections[fd]);
            }
            // 写回调是同样的道理
            if ((event & EPOLLOUT) && (IsConnectionSafe(fd)))
            {
                if (_connections[fd]->_send_cb)
                    _connections[fd]->_send_cb(_connections[fd]);
            }
        }
    }

    void Start()
    {
        // 启动服务quit设置服务器状态
        _quit = false;
        while (!_quit)
        {
            // 调用事件派发器
            Dispatcher();
            // PrintDebug();
        }
        // 服务器状态
        _quit = true;
    }

    void PrintDebug()
    {
        for (auto &ch : _connections)
        {
            cout << ch.second->Getsock() << ",inbuffer" << ch.second->Inbuffer().c_str();
        }
        cout << endl;
    }

    ~TcpServer()
    {
    }

private:
    // _epoller_ptr,_listensock_ptr,
    std::shared_ptr<Epoller> _epoller_ptr;
    std::shared_ptr<Sock> _listensock_ptr;
    // 接收就绪队列的数组
    struct epoll_event _revs[maxnum];

    // 从一个文件描述符到一个epoller链接的映射。服务器管理的所有的链接
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;

    // 端口号，服务器状态
    uint16_t _port;
    bool _quit;

    // 上层处理回调
    func_t _OnMessage;
};